package com.xia.minio.util;

import com.xia.minio.entity.FileItem;
import io.minio.*;
import io.minio.messages.Bucket;
import io.minio.messages.Item;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.*;

@Slf4j
@Component
@RequiredArgsConstructor
public class MinioUtil {

    private final MinioClient minioClient;

    /**
     * 查看存储bucket是否存在
     *
     * @return boolean
     */
    public Boolean bucketExists(String bucketName) {
        try {
            return minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
        } catch (Exception e) {
            log.error("查询bucket失败", e);
            return false;
        }
    }
    
    /**
     * 下载文件
     *
     * @param bucketName bucket名称
     * @param objectName 对象名称（包含路径）
     * @return 文件流
     */
    public GetObjectResponse downloadFile(String bucketName, String objectName) {
        try {
            return minioClient.getObject(
                    GetObjectArgs.builder()
                            .bucket(bucketName)
                            .object(objectName)
                            .build());
        } catch (Exception e) {
            log.error("下载文件失败,", e);
            return null;
        }
    }

    /**
     * 创建存储bucket
     */
    public void makeBucket(String bucketName) {
        try {
            minioClient.makeBucket(MakeBucketArgs.builder()
                    .bucket(bucketName)
                    .build());
        } catch (Exception e) {
            log.error("创建bucket失败,", e);
        }
    }

    /**
     * 列出所有bucket
     */
    public List<Bucket> listBuckets() {
        List<Bucket> buckets = new ArrayList<>();
        try {
            buckets = minioClient.listBuckets();
        }catch (Exception e){
            log.error("查询bucket失败,", e);
        }
        return buckets;
    }

    /**
     * 设置bucket权限为public
     */
    public void setBucketPublic(String bucketName) {
        try {
            // 设置公开
            String sb = "{\"Version\":\"2012-10-17\"," +
                    "\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":" +
                    "{\"AWS\":[\"*\"]},\"Action\":[\"s3:ListBucket\",\"s3:ListBucketMultipartUploads\"," +
                    "\"s3:GetBucketLocation\"],\"Resource\":[\"arn:aws:s3:::" + bucketName +
                    "\"]},{\"Effect\":\"Allow\",\"Principal\":{\"AWS\":[\"*\"]},\"Action\":[\"s3:PutObject\",\"s3:AbortMultipartUpload\",\"s3:DeleteObject\",\"s3:GetObject\",\"s3:ListMultipartUploadParts\"],\"Resource\":[\"arn:aws:s3:::" +
                    bucketName +
                    "/*\"]}]}";
            minioClient.setBucketPolicy(
                    SetBucketPolicyArgs.builder()
                            .bucket(bucketName)
                            .config(sb)
                            .build());
        } catch (Exception e) {
            log.error("创建bucket失败,", e);
        }
    }

    /**
     * 文件上传
     *
     * @param bucketName bucket名称
     * @param path       路径
     * @param file       文件
     * @return Boolean
     */
    public String upload(String bucketName, String path, MultipartFile file) {
        String originalFilename = file.getOriginalFilename();
        if (StringUtils.isBlank(originalFilename)) {
            throw new RuntimeException();
        }
        String fileName = System.currentTimeMillis() + "";
        if (originalFilename.lastIndexOf(".") >= 0) {
            fileName += originalFilename.substring(originalFilename.lastIndexOf("."));
        }
        String objectName = DateTimeUtils.getFormatDate(new Date(), DateTimeUtils.PARTDATEFORMAT) + "/" + fileName;
        try {
            InputStream stream = new ByteArrayInputStream(file.getBytes());
            PutObjectArgs objectArgs = PutObjectArgs.builder().bucket(bucketName).object(path + "/" + objectName)
                .stream(stream, file.getSize(), -1).contentType(file.getContentType()).build();
            //文件名称相同会覆盖
            minioClient.putObject(objectArgs);
        } catch (Exception e) {
            log.error("上传图片失败,", e);
            return null;
        }
        return objectName;
    }

    /**
     * 文件上传
     *
     * @param bucketName  bucket名称
     * @param path        路径
     * @param name        文件名
     * @param fileByte    文件内容
     * @param contentType contentType
     * @return objectName
     */
    public String upload(String bucketName, String path, String name, byte[] fileByte, String contentType) {

        String fileName = System.currentTimeMillis() + name.substring(name.lastIndexOf("."));
        String objectName = DateTimeUtils.getFormatDate(new Date(), DateTimeUtils.PARTDATEFORMAT) + "/" + fileName;
        try {
            InputStream stream = new ByteArrayInputStream(fileByte);
            PutObjectArgs objectArgs = PutObjectArgs.builder().bucket(bucketName).object(path + "/" + objectName)
                    .stream(stream, fileByte.length, -1).contentType(contentType).build();
            //文件名称相同会覆盖
            minioClient.putObject(objectArgs);
        } catch (Exception e) {
            log.error("上传文件失败,", e);
            return null;
        }
        return objectName;
    }


    /**
     * 创建文件夹
     *
     * @param bucketName bucket名称
     * @param folderPath 文件夹路径（以/结尾）
     * @return true/false
     */
    public boolean createFolder(String bucketName, String folderPath) {
        try {
            // 确保文件夹路径以/结尾
            String folderKey = folderPath;
            if (!folderKey.endsWith("/")) {
                folderKey = folderKey + "/";
            }
            
            // 在MinIO中创建文件夹，实际上是创建一个以/结尾的空对象
            minioClient.putObject(
                    PutObjectArgs.builder()
                            .bucket(bucketName)
                            .object(folderKey)
                            .stream(new ByteArrayInputStream(new byte[0]), 0, -1)
                            .build()
            );
            return true;
        } catch (Exception e) {
            log.error("创建文件夹失败, folderPath={}", folderPath, e);
            return false;
        }
    }

    /**
     * 删除
     *
     * @param bucketName bucket名称
     * @param path       路径
     * @param fileName   文件名
     * @return true/false
     */
    public boolean remove(String bucketName, String path, String fileName) {
        try {
            minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(path + fileName).build());
        } catch (Exception e) {
            log.error("删除文件失败,", e);
            return false;
        }
        return true;
    }

    /**
     * 列出目录下的文件和子目录（非递归，只返回当前层级）
     *
     * @param bucketName bucket名称
     * @param prefix     目录前缀
     * @return 文件和目录列表
     */
    public List<String> listFiles(String bucketName, String prefix) {
        List<String> fileList = new ArrayList<>();
        try {
            // 如果prefix不为空且不以/结尾，添加/以确保正确列出目录内容
            if (StringUtils.isNotBlank(prefix) && !prefix.endsWith("/")) {
                prefix = prefix + "/";
            }
            
            // 使用recursive=false只获取当前层级的文件和目录
            Iterable<Result<Item>> results = minioClient.listObjects(
                    ListObjectsArgs.builder()
                            .bucket(bucketName)
                            .prefix(prefix)
                            .recursive(false)
                            .build());
            
            for (Result<Item> result : results) {
                Item item = result.get();
                String objectName = item.objectName();
                
                // 如果是顶层查询（没有prefix），或者是prefix下的直接子项
                if (StringUtils.isEmpty(prefix) || objectName.startsWith(prefix)) {
                    // 对于非顶层查询，需要确保只添加当前层级的项
                    if (StringUtils.isNotEmpty(prefix)) {
                        // 去除prefix，检查剩余部分是否还有/（如果有，说明不是直接子项）
                        String relativePath = objectName.substring(prefix.length());
                        if (!relativePath.contains("/") || item.isDir()) {
                            fileList.add(objectName);
                        }
                    } else {
                        // 顶层查询，直接添加
                        fileList.add(objectName);
                    }
                }
            }
            
            // 按名称排序
            fileList.sort(String::compareTo);
        } catch (Exception e) {
            log.error("列出文件失败,", e);
        }
        return fileList;
    }
    
    /**
     * 列出目录下的文件和子目录，返回FileItem对象列表（非递归，只返回当前层级）
     *
     * @param bucketName bucket名称
     * @param prefix     目录前缀
     * @return FileItem对象列表
     */
    /**
     * 递归列出目录下的所有文件
     * 
     * @param bucketName bucket名称
     * @param prefix 目录前缀
     * @return 所有文件的路径列表
     */
    public List<String> listFilesRecursive(String bucketName, String prefix) {
        List<String> fileList = new ArrayList<>();
        try {
            // 如果prefix不为空且不以/结尾，添加/以确保正确列出目录内容
            if (StringUtils.isNotBlank(prefix) && !prefix.endsWith("/")) {
                prefix = prefix + "/";
            }
            
            // 使用recursive=true递归获取所有文件
            Iterable<Result<Item>> results = minioClient.listObjects(
                    ListObjectsArgs.builder()
                            .bucket(bucketName)
                            .prefix(prefix)
                            .recursive(true)
                            .build());
            
            for (Result<Item> result : results) {
                Item item = result.get();
                // 只添加文件，不添加文件夹
                if (!item.isDir()) {
                    fileList.add(item.objectName());
                }
            }
        } catch (Exception e) {
            log.error("递归列出文件失败,", e);
        }
        return fileList;
    }
    
    /**
     * 列出目录下的文件和子目录，返回FileItem对象列表（非递归，只返回当前层级）
     * 
     * @param bucketName bucket名称
     * @param prefix 目录前缀
     * @return FileItem对象列表
     */
    public List<FileItem> listFileItems(String bucketName, String prefix) {
        List<FileItem> fileItems = new ArrayList<>();
        try {
            // 如果prefix不为空且不以/结尾，添加/以确保正确列出目录内容
            if (StringUtils.isNotBlank(prefix) && !prefix.endsWith("/")) {
                prefix = prefix + "/";
            }
            
            // 使用recursive=false只获取当前层级的文件和目录
            Iterable<Result<Item>> results = minioClient.listObjects(
                    ListObjectsArgs.builder()
                            .bucket(bucketName)
                            .prefix(prefix)
                            .recursive(false)
                            .build());
            
            for (Result<Item> result : results) {
                Item item = result.get();
                String objectName = item.objectName();
                
                // 过滤条件：只添加当前层级的项
                boolean shouldAdd = false;
                if (StringUtils.isEmpty(prefix)) {
                    shouldAdd = true;
                } else if (objectName.startsWith(prefix)) {
                    String relativePath = objectName.substring(prefix.length());
                    shouldAdd = !relativePath.contains("/") || item.isDir();
                }
                
                if (shouldAdd) {
                    FileItem fileItem = new FileItem();
                    
                    // 设置名称（去掉路径前缀）
                    String name;
                    if (item.isDir()) {
                        // 目录：如果不是根目录下的，去掉最后一个/；对于子目录，使用相对名称
                        if (StringUtils.isEmpty(prefix)) {
                            name = objectName.substring(0, objectName.length() - 1);
                        } else {
                            name = objectName.substring(prefix.length(), objectName.length() - 1);
                        }
                    } else {
                        // 文件：使用最后一个/后面的部分作为名称
                        if (objectName.contains("/")) {
                            name = objectName.substring(objectName.lastIndexOf("/") + 1);
                        } else {
                            name = objectName;
                        }
                    }
                    
                    fileItem.setName(name);
                    fileItem.setPath(objectName);
                    fileItem.setDirectory(item.isDir());
                    fileItem.setSize(item.size());
                    if(!item.isDir()){
                        fileItem.setLastModified(item.lastModified().toString());
                    }

                    
                    // 设置文件类型（扩展名）
                    if (!item.isDir() && name.contains(".")) {
                        fileItem.setType(name.substring(name.lastIndexOf(".") + 1).toLowerCase());
                    } else {
                        fileItem.setType(item.isDir() ? "folder" : "file");
                    }
                    
                    fileItems.add(fileItem);
                }
            }
            
            // 按类型和名称排序：目录在前，文件在后，同类型按名称排序
            fileItems.sort((a, b) -> {
                if (a.isDirectory() && !b.isDirectory()) {
                    return -1;
                } else if (!a.isDirectory() && b.isDirectory()) {
                    return 1;
                } else {
                    return a.getName().compareTo(b.getName());
                }
            });
        } catch (Exception e) {
            log.error("列出文件项失败,", e);
        }
        return fileItems;
    }
}
